package finalproject.controller.commands;

import java.lang.reflect.InvocationTargetException;

import finalproject.model.player.Player;

/**
 * This class is part of the "World of Zuul" application. "World of Zuul" is a
 * very simple, text based adventure game.
 *
 * This class holds information about a command that was issued by the user. A
 * command currently consists of two parts: a CommandWord and a string (for
 * example, if the command was "take map", then the two parts are TAKE and
 * "map").
 * 
 * The way this is used is: Commands are already checked for being valid command
 * words. If the user entered an invalid command (a word that is not known) then
 * the CommandWord is UNKNOWN.
 *
 * If the command had only one word, then the second word is <null>.
 * 
 * @author Michael Kölling and David J. Barnes
 * @version 2011.08.10
 */

public abstract class Command {

	/** Default unknown command */
	public static final String UNKNOWN_COMMAND = "Unknown";

	/** Commands list (impossible to get classes in a package) */
	public static final String[] COMMANDS = { "back", "drop", "energy", "give", "go", "help", "interact", "item",
			"light", "quit", "take", "use" };

	/** Player invoker */
	private Player player;

	/** Command arguments */
	private String[] args;

	/**
	 * Create a command object. First and second words must be supplied, but the
	 * second may be null.
	 * 
	 * @param player
	 *            The player.
	 * @param commandArgs
	 *            The second word of the command. May be null.
	 */
	public Command(Player player, String[] commandArgs)
	{
		this.player = player;
		this.args = commandArgs;
	}

	/**
	 * Execute the command.
	 * 
	 * @return <code>true</code> if exit after this command, <code>false</code>
	 *         otherwise
	 */
	public abstract boolean process();

	/**
	 * Return the player.
	 * 
	 * @return player
	 */
	public Player getPlayer()
	{
		return player;
	}

	/**
	 * Return the number of arguments.
	 * 
	 * @return number of arguments
	 */
	public int getNbArguments()
	{
		return (args == null ? 0 : args.length);
	}

	/**
	 * Return an argument by its number.
	 * 
	 * @param number
	 *            argument number
	 * @return argument
	 */
	public String getArgument(int number)
	{
		return number < 1 || number > getNbArguments() ? null : args[number - 1];
	}

	/**
	 * @return The second word of this command. Returns null if there was no
	 *         second word.
	 */
	public String getSecondWord()
	{
		return getArgument(1);
	}

	/**
	 * @return true if the command has a second word.
	 */
	public boolean hasSecondWord()
	{
		return getNbArguments() >= 1;
	}

	/**
	 * Create and return the command, based on the name and its arguments.
	 * 
	 * @param player
	 *            player invoker
	 * @param command
	 *            command name
	 * @param args
	 *            command arguments
	 * @return new command
	 */
	public static Command getCommand(Player player, String command, String... args)
	{
		// Create the command class name
		String commandClassName = command.toUpperCase().charAt(0) + command.substring(1).toLowerCase() + "Command";

		try
		{
			// Get the class by its full name (concat package name)
			Class<? extends Command> commandClass = Class.forName(
					Command.class.getPackage().getName() + "." + commandClassName).asSubclass(Command.class);

			// Instanciate a new command
			return commandClass.getConstructor(Player.class, String[].class).newInstance(player, args);
		}
		catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException
				| InvocationTargetException | NoSuchMethodException | SecurityException e)
		{
			// Command doesn't exists
			// e.printStackTrace();
			return getCommand(player, UNKNOWN_COMMAND);
		}

		// Unknown command

	}
}
